﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Text;
using System.Web.UI;
using Ext.Net;
using Ext.Net.Utilities;
using KamLib.Converts;

namespace KamLib
{

    public interface IListLogicContainer : IEquipContainer
    {
        ListLogic Logic { get; set; }
        void Initialize();
    }

    public abstract class ListLogic : UilNode
    {
        public string SortField;
        public SortDirection SortDir = SortDirection.DESC;
        public int PageSize = 25;
        public string GroupField;

        public bool CanInsert = true;
        public bool CanEdit = true;
        public bool CanDelete = true;

        public bool CanModify
        {
            get { return CanInsert || CanEdit || CanDelete; }
            set { CanInsert = CanEdit = CanDelete = value; }
        }

        public bool CanEditOnly
        {
            get { return CanEdit && !CanInsert && !CanDelete; }
            set
            {
                CanEdit = value;
                if (value)
                    CanInsert = CanDelete = false;
            }
        }

        public bool Creating = true;

        public virtual void AddFields(IStoreContainer istore) { }

        /// <summary>
        /// Сохраняет изменения в базу.
        /// </summary>
        /// <param name="newData">Новые данные.</param>
        public virtual void SaveLocalChanges(IEnumerable<object> newData) { throw new NotImplementedException(); }

        /// <summary>
        /// Для мониторинга кол-ва ошибок
        /// </summary>
        public int[] Errors;

        /// <summary>
        /// Задаётся из-вне для записи из SelectErrors
        /// </summary>
        public StringBuilder ErrorLog { get; set; }

        public string Script;

        public override void InitCheck()
        {
            if (KeyFields == null)
                KeyFields = new[] { "ID" };
        }

        public abstract IEnumerable<object> Select(WhereArgs e);
        public virtual IEnumerable<object> SelectArray(WhereArgs e)
        {
            return Select(e).ToArray();
        }

        public override object GetResyncData(UilResyncKey key)
        {
            var r = key.Key != null && key.Key.ID != null
                ? key.Mode == UilResyncMode.Delete
                    ? new { delId = key.Key.ID.First().Value }
                    : Select(new WhereArgs(Controller.Context, key.Key.ID)).FirstOrDefault()
                : null;
            if (key.Key != null) key.Key.ID = null;
            return r;
        }

        public virtual void EquipingControl(Control parent, IEquipContainer c)
        {
            var frm = Controller.FormInfos.Try(LIID.TrimEnd(false, "List") + "Form");
            if (frm != null)
                FormGridLogicAdapter.Apply(this, frm, c.Owner.Get<IGridContainer>());
        }

        public WhereArgs NewWhereArgs()
        {
            return new WhereArgs(Controller.Context)
            {
                Start = 0,
                Limit = -1,
                SortFields = 
                { 
                    new WhereArgSortField 
                    { 
                        Name = SortField.IfNull(KeyField),
                        Dir = SortDir,
                    }
                },
                TotalExpected = true,
            };

        }

        public IEnumerable<object> Select() { return Select(NewWhereArgs()); }
        public IEnumerable<object> SelectArray() { return SelectArray(NewWhereArgs()); }


        public Control Render(string id, Control parent, Control container, RenderMode mode)
        {
            return XControlResponse.Render(
                Controller.VirtualPath + LIID + ".ascx", Controller.LID + "_" + id,
                c =>
                {
                    var ilogic = c as IListLogicContainer;
                    if (ilogic != null)
                    {
                        ilogic.Logic = this;
                        ilogic.Initialize();
                    }

                    var ec = c as IEquipContainer;
                    if (ec == null) return null;

                    EquipingControl(parent, ec);

                    var igrid = ec.Owner.Get<IGridContainer>();
                    return igrid != null ? igrid.Grid : null;
                },
                mode, container.ClientID
            );
        }

    }

    public abstract class ListLogic<TListData> : ListLogic
        where TListData : class, new()
    {

        public override void AddFields(IStoreContainer istore)
        {
            if (typeof(TListData) != typeof(Dictionary<string, object>))
                istore.Store.Reader[0].AddFields(typeof(TListData));
        }

        public override IEnumerable<object> Select(WhereArgs e)
        {
            Activate();
            return SelectEntities(e).ToList();
        }
        public override IEnumerable<object> SelectArray(WhereArgs e)
        {
            Activate();
            return SelectEntities(e).ToArray();
        }

        public abstract IEnumerable<TListData> SelectEntities(WhereArgs e);

        public IEnumerable<TListData> Empty()
        {
            return new TListData[] { };
        }

    }


    public abstract class SqlListLogic : ListLogic<Dictionary<string, object>>
    {
        public IDbConnection cn;
        public ListLogicSql Sql;
        public int RowNumbererWidth;


        protected virtual DbConnection NewConnection()
        {
            throw new NotImplementedException();
        }

        public override void AfterInit()
        {
            CanModify = false;
            base.AfterInit();
        }

        public override void Activate()
        {
            if (cn == null)
                cn = NewConnection();
        }
        public override void Dispose()
        {
            if (cn == null) return;
            cn.Dispose();
            cn = null;
        }

        protected virtual void NormalizeColumn(Column col) { }

        public override void AddFields(IStoreContainer istore)
        {
            Activate();

            var igrid = istore.Owner.Get<IGridContainer>();
            var cols = igrid != null ? igrid.Grid.ColumnModel.Columns : null;
            var rdr = istore.Store.Reader[0];

            var addCols = cols != null && cols.Count == 0;

            if (cols != null && RowNumbererWidth > 0)
                cols.Insert(0, new RowNumbererColumn { Width = RowNumbererWidth });


            if (Sql.FieldDescriptor != null)
            {
                foreach (var prop in Sql.FieldDescriptor.GetPropertiesAndFields())
                {
                    var type = SystemDataHelper.NormalizeType(prop.ResultType());
                    rdr.Fields.Add(new RecordField(prop.Name, ExtUtils.TypeToFieldType(type)) { DateFormat = "M$" });

                    if (!addCols) continue;
                    var col = ExtUtils.NewColumn(type);
                    col.DataIndex = col.Header = prop.Name;
                    NormalizeColumn(col);
                    cols.Add(col);

                }
            }
            else
            {
                if (Sql.FieldSql.IsEmpty())
                {
                    if (Sql.Sql.IsEmpty()) return;
                    Sql.FieldSql = Sql.Sql.Replace("{ORDERBY}", "").Replace("{LIMIT}", "");
                }

                using (var r = cn.NewDataReader(Sql.FieldSql))
                {
                    for (var i = 0; i < r.FieldCount; ++i)
                    {
                        var name = r.GetName(i);
                        var type = SystemDataHelper.NormalizeType(r.GetFieldType(i));
                        rdr.Fields.Add(new RecordField(name, ExtUtils.TypeToFieldType(type)) { DateFormat = "M$" });

                        if (!addCols) continue;
                        var col = ExtUtils.NewColumn(type);
                        col.DataIndex = col.Header = name;
                        NormalizeColumn(col);
                        cols.Add(col);
                    }
                }
            }
        }

        public override IEnumerable<Dictionary<string, object>> SelectEntities(WhereArgs e)
        {
            Activate();

            if (Sql.CountSql.IsNotEmpty())
                e.Total = cn.ExecScalar(Sql.CountSql).ToInt();

            return Sql.Sql.IsEmpty() ? null : cn.Select(Sql.FieldDescriptor, Sql.Sql
                .Replace("{ORDERBY}", e.SortFields.Count == 0 ? null : "ORDER BY " + e.SortFields.ToSql())
                .Replace("{LIMIT}", e.Start >= 0 && e.Limit >= 0 ? "LIMIT " + e.Start + ", " + e.Limit : null)
            );
        }

    }
}